package com.smartinput.voice.application.service

import com.smartinput.voice.config.AudioConfig
import com.smartinput.voice.domain.repository.VoiceRecordRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.FileOutputStream
import java.time.LocalDateTime
import java.util.concurrent.TimeUnit

@Service
class VoiceRecognitionService(
    private val voiceRecordRepository: VoiceRecordRepository,
    private val speechRecognitionManager: SpeechRecognitionManager,
    private val audioConfig: AudioConfig
) {

    private val logger = LoggerFactory.getLogger(VoiceRecognitionService::class.java)

    // 音频文件保存路径
    private val audioSaveDir = File(audioConfig.saveDir)

    fun processAudioStream(audioData: ByteArray, isRealTime: Boolean = false): String {
        try {
            // 保存并转换音频，获取WAV数据
            val wavBytes = saveAndConvertAudio(audioData, "stream_audio")
            
            // 使用转换后的WAV数据进行识别
            val result = speechRecognitionManager.recognizeAudio(wavBytes, isRealTime)
            return if (result.success) {
                result.text
            } else {
                "语音识别失败: ${result.error}"
            }
        } catch (e: Exception) {
            logger.error("处理音频流失败", e)
            return "语音识别失败: ${e.message}"
        }
    }

    fun processAudioFile(userId: String, deviceId: String, audioFile: MultipartFile): String {
        try {
            val audioData = audioFile.bytes
            
            // 保存并转换音频，获取WAV数据
            val wavBytes = saveAndConvertAudio(audioData, deviceId)
            
            // 使用转换后的WAV数据进行识别（普通录入模式，非实时）
            val result = speechRecognitionManager.recognizeAudio(wavBytes, isRealTime = false)

            val recognizedText = if (result.success) {
                result.text
            } else {
                "语音识别失败: ${result.error}"
            }

            // 保存语音记录
            saveVoiceRecord(userId, deviceId, recognizedText, result.confidence)

            return recognizedText
        } catch (e: Exception) {
            logger.error("处理音频文件失败", e)
            return "语音识别失败: ${e.message}"
        }
    }

    /**
     * 保存音频并转换为WAV格式，返回WAV字节数据
     * @param audioData 原始音频数据
     * @param deviceId 设备ID
     * @return 转换后的WAV字节数据
     */
    private fun saveAndConvertAudio(audioData: ByteArray, deviceId: String): ByteArray {
        if (!audioConfig.saveEnabled) {
            logger.debug("音频文件保存已禁用，直接转换音频数据")
            // 即使不保存，也要转换格式
            return convertAudioDataToWav(audioData, deviceId)
        }

        try {
            // 确保目录存在
            if (!audioSaveDir.exists()) {
                audioSaveDir.mkdirs()
                logger.info("创建音频文件保存目录: ${audioSaveDir.absolutePath}")
            }

            // 使用固定的文件名，覆盖之前的文件
            val originalFileName = "mobile_audio_original.wav"
            val convertedFileName = "mobile_audio_converted.wav"
            val audioFile = File(audioSaveDir, originalFileName)
            val wavFile = File(audioSaveDir, convertedFileName)

            // 保存原始音频文件（覆盖）
            FileOutputStream(audioFile).use { fos ->
                fos.write(audioData)
            }

            logger.info("原始音频文件已保存到本地: ${audioFile.absolutePath}")
            logger.info("音频文件大小: ${audioData.size} bytes")
            logger.info("设备ID: $deviceId")

            // 检查文件格式并转换为WAV
            val fileFormat = detectAudioFormat(audioFile)
            val finalWavFile = if (fileFormat != "wav") {
                logger.info("检测到音频格式: $fileFormat，开始转换为WAV格式")
                val convertedFile = convertToWav(audioFile, wavFile)
                logger.info("音频文件已转换为WAV格式: ${convertedFile.absolutePath}")
                convertedFile
            } else {
                logger.info("音频文件已经是WAV格式，复制到固定位置")
                // 如果已经是WAV格式，直接复制到固定位置
                audioFile.copyTo(wavFile, overwrite = true)
                wavFile
            }

            // 读取WAV文件并返回字节数据
            val wavBytes = finalWavFile.readBytes()
            logger.info("返回WAV音频数据，大小: ${wavBytes.size} bytes")
            return wavBytes

        } catch (e: Exception) {
            logger.error("保存音频文件失败，尝试直接转换", e)
            // 如果保存失败，直接转换音频数据
            return convertAudioDataToWav(audioData, deviceId)
        }
    }

    /**
     * 直接转换音频数据为WAV格式
     * @param audioData 原始音频数据
     * @param deviceId 设备ID
     * @return 转换后的WAV字节数据
     */
    fun convertAudioDataToWav(audioData: ByteArray, deviceId: String): ByteArray {
        try {
            // 创建临时目录
            val tempDir = java.nio.file.Files.createTempDirectory("audio_convert")
            val inputFile = File(tempDir.toFile(), "temp_audio_input")
            val outputFile = File(tempDir.toFile(), "temp_audio_output.wav")

            // 写入原始音频数据
            FileOutputStream(inputFile).use { fos ->
                fos.write(audioData)
            }

            logger.info("创建临时音频文件: ${inputFile.absolutePath}")

            // 转换为WAV格式
            val convertedFile = convertToWav(inputFile, outputFile)
            
            // 读取转换后的WAV数据
            val wavBytes = convertedFile.readBytes()
            
            // 清理临时文件
            inputFile.delete()
            convertedFile.delete()
            tempDir.toFile().deleteRecursively()
            
            logger.info("音频数据转换成功，WAV大小: ${wavBytes.size} bytes")
            return wavBytes
            
        } catch (e: Exception) {
            logger.error("音频数据转换失败", e)
            throw RuntimeException("音频格式转换失败: ${e.message}")
        }
    }

    /**
     * 检测音频文件格式
     */
    private fun detectAudioFormat(file: File): String {
        return try {
            val processBuilder = ProcessBuilder("file", file.absolutePath)
            val process = processBuilder.start()
            val output = process.inputStream.bufferedReader().readText()

            when {
                output.contains("WAVE audio") -> "wav"
                output.contains("WebM") -> "webm"
                output.contains("MPEG") -> "mp3"
                output.contains("FLAC") -> "flac"
                output.contains("OGG") -> "ogg"
                else -> "unknown"
            }
        } catch (e: Exception) {
            logger.warn("无法检测音频格式，使用文件扩展名: ${e.message}")
            file.extension.lowercase()
        }
    }

    /**
     * 将音频文件转换为WAV格式
     */
    private fun convertToWav(inputFile: File, outputFile: File): File {
        val processBuilder = ProcessBuilder(
            "ffmpeg",
            "-i", inputFile.absolutePath,
            "-acodec", "pcm_s16le",
            "-ar", "16000",
            "-ac", "1",
            "-y", // 覆盖输出文件
            outputFile.absolutePath
        )

        processBuilder.redirectErrorStream(true)
        val process = processBuilder.start()

        // 读取进程输出
        val output = process.inputStream.bufferedReader().readText()
        logger.debug("FFmpeg转换输出: $output")

        // 等待进程完成，最多等待10秒
        val completed = process.waitFor(10, TimeUnit.SECONDS)

        if (!completed) {
            process.destroyForcibly()
            throw RuntimeException("音频格式转换超时")
        }

        if (process.exitValue() != 0) {
            throw RuntimeException("音频格式转换失败: ${process.exitValue()}")
        }

        logger.info("音频格式转换成功: ${outputFile.absolutePath}")
        return outputFile
    }

    fun saveVoiceRecord(
        userId: String,
        deviceId: String,
        content: String,
        confidence: Double
    ): com.smartinput.voice.application.dto.VoiceRecord {
        val voiceRecord = com.smartinput.voice.domain.model.VoiceRecord(
            uid = com.mensanyun.kotool.util.UlidUtil.generate(),
            userId = userId,
            deviceId = deviceId,
            content = content,
            confidence = confidence,
            enabled = true,
            createdAt = LocalDateTime.now()
        )

        val savedRecord = voiceRecordRepository.save(voiceRecord)

        return com.smartinput.voice.application.dto.VoiceRecord(
            id = savedRecord.uid,
            userId = savedRecord.userId,
            deviceId = savedRecord.deviceId,
            content = savedRecord.content,
            confidence = savedRecord.confidence,
            createdAt = savedRecord.createdAt.toString()
        )
    }

    fun getVoiceRecordsByUser(userId: String, limit: Int): List<com.smartinput.voice.application.dto.VoiceRecord> {
        val records = voiceRecordRepository.findByUserIdOrderByCreatedAtDesc(userId, limit)
        return records.map { record ->
            com.smartinput.voice.application.dto.VoiceRecord(
                id = record.uid,
                userId = record.userId,
                deviceId = record.deviceId,
                content = record.content,
                confidence = record.confidence,
                createdAt = record.createdAt.toString()
            )
        }
    }

    /**
     * 实时语音识别（WebSocket）
     * @param audioData 原始音频数据
     * @param deviceId 设备ID
     * @return 识别结果
     */
    fun recognizeAudioRealTime(audioData: ByteArray, deviceId: String): SpeechRecognitionManager.RecognitionResult {
        return try {
            logger.info("开始实时语音识别，音频大小: ${audioData.size} bytes")
            
            // 1. 格式转换
            val wavData = convertAudioDataToWav(audioData, deviceId)
            logger.info("音频格式转换完成，WAV大小: ${wavData.size} bytes")
            
            // 2. 格式校验
            if (!validateAudioData(wavData)) {
                logger.error("音频格式校验失败")
                return SpeechRecognitionManager.RecognitionResult(
                    text = "",
                    confidence = 0.0,
                    success = false,
                    error = "音频格式不符合阿里云要求"
                )
            }
            
            // 3. 调用SpeechRecognitionManager进行实时识别
            val result = speechRecognitionManager.recognizeAudio(wavData, isRealTime = true)
            
            logger.info("实时语音识别完成，结果: ${result.text}")
            result
            
        } catch (e: Exception) {
            logger.error("实时语音识别失败", e)
            SpeechRecognitionManager.RecognitionResult(
                text = "",
                confidence = 0.0,
                success = false,
                error = e.message
            )
        }
    }

    /**
     * 验证音频数据格式是否符合阿里云要求
     * 要求：16kHz采样率，单声道，16位PCM，WAV格式
     */
    private fun validateAudioData(audioData: ByteArray): Boolean {
        try {
            // 检查WAV文件头
            if (audioData.size < 44) {
                logger.warn("音频数据太小，可能不是有效的WAV文件")
                return false
            }
            
            // 检查RIFF标识
            val riffHeader = String(audioData, 0, 4)
            if (riffHeader != "RIFF") {
                logger.warn("无效的WAV文件头，RIFF标识: $riffHeader")
                return false
            }
            
            // 检查WAVE标识
            val waveHeader = String(audioData, 8, 4)
            if (waveHeader != "WAVE") {
                logger.warn("无效的WAV文件头，WAVE标识: $waveHeader")
                return false
            }
            
            // 检查音频格式 (PCM = 1)
            val format = audioData[20].toInt() and 0xFF
            if (format != 1) {
                logger.warn("不支持的音频格式: $format (PCM=1)")
                return false
            }
            
            // 检查声道数 (单声道 = 1)
            val channels = audioData[22].toInt() and 0xFF
            if (channels != 1) {
                logger.warn("不支持的声道数: $channels (单声道=1)")
                return false
            }
            
            // 检查采样率 (16000Hz)
            val sampleRate = (audioData[24].toInt() and 0xFF) or
                            ((audioData[25].toInt() and 0xFF) shl 8) or
                            ((audioData[26].toInt() and 0xFF) shl 16) or
                            ((audioData[27].toInt() and 0xFF) shl 24)
            if (sampleRate != 16000) {
                logger.warn("不支持的采样率: $sampleRate Hz (要求: 16000 Hz)")
                return false
            }
            
            // 检查位深度 (16位)
            val bitsPerSample = audioData[34].toInt() and 0xFF
            if (bitsPerSample != 16) {
                logger.warn("不支持的位深度: $bitsPerSample bits (要求: 16 bits)")
                return false
            }
            
            logger.info("音频数据格式验证通过: ${sampleRate}Hz, ${channels}声道, ${bitsPerSample}位PCM")
            return true
            
        } catch (e: Exception) {
            logger.error("音频数据格式验证失败", e)
            return false
        }
    }
} 